﻿/*******************************************************
Copyright © 2008-2009 Active Technologies Group, Inc. (ATGi)

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*******************************************************/
using System;
using System.IO;
using System.Linq;
using System.Xml;
using ATGi.CS.Components;
using CommunityServer.Components;
using NUnit.Framework;
using Rhino.Mocks;
using PrivateObject = Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject;
using PrivateType = Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType;

namespace ATGi.CS.Test.Components
{
    [TestFixture]
    public class VideoPreviewImageModuleTests
    {
        [Test, Combinatorial]
        public void GetPreviewFile_Attachment(
            [Values(1, 123, int.MaxValue)] int postID,
            [Values("video.flv")] string fileName)
        {
            // Arrange
            MockRepository mocks = new MockRepository();

            var cfsProvider = mocks.StrictMock<ICentralizedFileStorageProvider>();

            using (mocks.Ordered())
            {
                Expect.Call(cfsProvider.MakePath(postID)).Return(postID.ToString());
                Expect.Call(cfsProvider.GetFile(postID.ToString(), "video.jpg")).Return(mocks.Stub<ICentralizedFile>());
            }

            VideoPreviewImageModule_Accessor.CurrentCentralizedFileStorageProvider = cfsProvider;

            PostAttachment attachment = new PostAttachment();
            attachment.PostID = postID;
            attachment.FileName = fileName;

            // Act
            mocks.ReplayAll();
            ICentralizedFile result = VideoPreviewImageModule.GetPreviewFile(attachment);

            // Assert
            mocks.VerifyAll();
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void GetPreviewFile_Attachment_Null()
        {
            // Arrange
            PostAttachment attachment = null;

            // Act
            ICentralizedFile result = VideoPreviewImageModule.GetPreviewFile(attachment);

            // Assert (test attribute handles assertion)
        }

        [Test]
        public void GetPreviewFile_Attachment_ZeroPostID()
        {
            // Arrange
            PostAttachment attachment = new PostAttachment();
            attachment.PostID = 0;

            // Act
            ICentralizedFile result = VideoPreviewImageModule.GetPreviewFile(attachment);

            // Assert
            Assert.IsNull(result);
        }

        [Test, Combinatorial]
        public void GetPreviewFile_File(
            [Values("", "foo", "foo/bar")] string path,
            [Values("video.flv")] string fileName)
        {
            // Arrange
            MockRepository mocks = new MockRepository();

            var cfsProvider = mocks.StrictMock<ICentralizedFileStorageProvider>();
            Expect.Call(cfsProvider.GetFile(path, "video.jpg")).Return(mocks.Stub<ICentralizedFile>());

            VideoPreviewImageModule_Accessor.CurrentCentralizedFileStorageProvider = cfsProvider;

            var file = mocks.Stub<ICentralizedFile>();
            Expect.Call(file.Path).Return(path);
            Expect.Call(file.FileName).Return(fileName);

            // Act
            mocks.ReplayAll();
            ICentralizedFile result = VideoPreviewImageModule.GetPreviewFile(file);

            // Assert
            mocks.VerifyAll();
        }

        [Test]
        public void GetPreviewFile_File_Null()
        {
            // Arrange
            ICentralizedFile file = null;

            // Act
            ICentralizedFile result = VideoPreviewImageModule.GetPreviewFile(file);

            // Assert
            Assert.IsNull(result);
        }

        [Test, Combinatorial]
        public void GetPreviewFileName(
            [Values("http://example.com/video.flv", "http://example.com/video.flv?foo=bar", "video.flv", "video")] string videoFileName)
        {
            // Arrange

            // Act
            string result = VideoPreviewImageModule_Accessor.GetPreviewFileName(videoFileName);

            // Assert
            Assert.AreEqual("video.jpg", result);
        }

        [Test, Combinatorial]
        public void CreatePreviewFile(
            [Values(null, 15)] int? startTimeOffset)
        {
            // Arrange
            int postID = 123;
            string fileName = "video.flv";

            string tempFilePath = @"D:\temp\Videos\";
            string ffmpegPath = @"D:\Video\";

            // - Create derived test arguments.
            string tempFolderPath = null;
            string tempVideoFilePath = null;
            string tempPreviewFilePath = null;
            string attachmentPath = postID.ToString();
            string previewFileName = "video.jpg";

            MockRepository mocks = new MockRepository();
            
            // - Create arguments for the call.
            var post = mocks.Stub<Post>();
            
            post.PostID = postID;

            if (startTimeOffset.HasValue)
                post.SetVideoPreviewImageTimeOffset(startTimeOffset.Value);

            post.Attachment = new PostAttachment();
            post.Attachment.PostID = postID;
            post.Attachment.FileName = fileName;

            var memoryStream = new MemoryStream();

            // - Create dependent mocks, including stub implementations on the target object.
            var cfsProvider = mocks.StrictMock<ICentralizedFileStorageProvider>();
            var fs = mocks.StrictMock<IFileSystemWrapper>();
            var target = mocks.PartialMock<VideoPreviewImageModule>();            

            Expect.Call(delegate { target.HandleException(null, null); }).IgnoreArguments().WhenCalled(mi => { throw new Exception("Mock exception.", (Exception)mi.Arguments[1]); }).Repeat.Any();

            using (mocks.Ordered())
            {
                Expect.Call(() => fs.CreateDirectory(null)).IgnoreArguments().Do(new Action<string>(path => 
                    { 
                        tempFolderPath = path;
                        tempVideoFilePath = Path.Combine(path, post.PostID.ToString());
                        tempPreviewFilePath = Path.Combine(path, post.PostID.ToString() + ".jpg");
                    }));
                Expect.Call(cfsProvider.MakePath(post.PostID)).Return(attachmentPath);
                Expect.Call(target.OpenAttachmentStream(post.Attachment)).Return(memoryStream);
                Expect.Call(() => fs.WriteStreamToFile(null, null)).IgnoreArguments().Do(new Action<Stream, string>((stream, path) => 
                    {
                        Assert.AreEqual(memoryStream, stream);
                        Assert.AreEqual(tempVideoFilePath, path);
                    }));
                Expect.Call(()=>target.CreateTemporaryPreviewFile(0, null, null)).IgnoreArguments().Do(new Action<int, string, string>((offset, videoPath, previewPath) =>
                    {
                        Assert.AreEqual(startTimeOffset ?? -1, offset);
                        Assert.AreEqual(tempVideoFilePath, videoPath);
                        Assert.AreEqual(tempPreviewFilePath, previewPath);
                    }));
                Expect.Call(()=>target.DeleteExistingPreviewFilesFromCentralizedFileStorage(attachmentPath, previewFileName));
                Expect.Call(() => target.SavePreviewFileToCentralizedFileStorage(null, null, null)).IgnoreArguments().Do(new Action<string, string, string>((prevPath, attachPath, prevFile) =>
                    {
                        Assert.AreEqual(tempPreviewFilePath, prevPath);
                        Assert.AreEqual(attachmentPath, attachPath);
                        Assert.AreEqual(previewFileName, prevFile);
                    }));
                Expect.Call(()=>fs.DeleteDirectoryIfExists(null)).IgnoreArguments().Do(new Action<string>(path => Assert.AreEqual(tempFolderPath, path)));
            }

            // - Create a private accessor for the target object.
            VideoPreviewImageModule_Accessor.CurrentCentralizedFileStorageProvider = cfsProvider;

            VideoPreviewImageModule_Accessor accessor = new VideoPreviewImageModule_Accessor(new PrivateObject(target, new PrivateType(typeof(VideoPreviewImageModule))));
            accessor.CurrentFileSystemWrapper = fs;
            accessor._ffmpegPath = ffmpegPath;
            accessor._tempFilePath = tempFilePath;

            // Act
            mocks.ReplayAll();
            accessor.CreatePreviewFile(post);
            
            // Assert 
            mocks.VerifyAll();
        }

        [Test, Combinatorial]
        public void csa_PostPostUpdate(
            [Values(true, false)] bool nonNullContent,
            [Values(true, false)] bool postContent,
            [Values(true, false)] bool nonNullAttachment,
            [Values(PostMediaType.Video, PostMediaType.Audio)] PostMediaType mediaType,
            [Values(ObjectState.Create, ObjectState.Update, ObjectState.Delete)] ObjectState objectState)
        {
            // Arrange
            MockRepository mocks = new MockRepository();

            // - Create arguments for the call.
            IContent content;

            if (nonNullContent)
            {
                if (postContent)
                {
                    content = mocks.Stub<Post>();

                    if (nonNullAttachment)
                        (content as Post).Attachment = mocks.Stub<PostAttachment>();

                    (content as Post).PostMedia = mediaType;
                }
                else
                    content = mocks.Stub<IContent>();
            }
            else
                content = null;
            
            CSPostEventArgs e = new CSPostEventArgs(objectState, ApplicationType.Unknown, string.Empty);

            // - Create dependent mocks, including stub implementations on the target object.
            var target = mocks.PartialMock<VideoPreviewImageModule>();

            using (mocks.Ordered())
            {
                if (nonNullContent && postContent && nonNullAttachment && (mediaType == PostMediaType.Video) && (objectState == ObjectState.Create || objectState == ObjectState.Update))
                    Expect.Call(() => target.CreatePreviewFile(content as Post));
            }

            // Act
            mocks.ReplayAll();
            target.csa_PostPostUpdate(content, e);

            // Assert 
            mocks.VerifyAll();
        }

        [Test]
        public void ParseAndVerifyFFMpegPathAttribute(
            [Values(true, false)] bool nodeHasAttribute,
            [Values(true, false)] bool pathIsDirectory,
            [Values(true, false)] bool pathAsDirectoryExists,
            [Values(true, false)] bool pathAsDirectoryWithAppendedFileExists,
            [Values(true, false)] bool pathAsFileExists)
        {
            // Arrange
            MockRepository mocks = new MockRepository();

            // - Create arguments for the call.
            string filePath = Guid.NewGuid().ToString();

            var doc = new XmlDocument();
            var node = doc.CreateElement("foo");

            if (nodeHasAttribute)
            {
                var attribute = doc.CreateAttribute("ffmpegPath");
                attribute.Value = filePath;
                node.Attributes.Append(attribute);
            }

            // - Create dependent mocks.
            var fs = mocks.StrictMock<IFileSystemWrapper>();
            var cntx = mocks.StrictMock<ICSContextWrapper>();
            var target = new VideoPreviewImageModule();
            
            VideoPreviewImageModule_Accessor accessor = new VideoPreviewImageModule_Accessor(new PrivateObject(target));
            accessor.CurrentFileSystemWrapper = fs;
            accessor.CurrentCSContextWrapper = cntx;

            if (nodeHasAttribute == false)
                Expect.Call(cntx.MapPath("~/bin/")).Return(filePath);

            if (pathIsDirectory)
            {
                if (pathAsDirectoryExists)
                {
                    if (pathAsDirectoryWithAppendedFileExists)
                    {
                        Expect.Call(fs.FileExists(filePath)).Return(false);
                        Expect.Call(fs.DirectoryExists(filePath)).Return(true);
                        Expect.Call(fs.FileExists(Path.Combine(filePath, "ffmpeg.exe"))).Return(true);
                        Expect.Call(() => fs.DemandFileIOPermission(System.Security.Permissions.FileIOPermissionAccess.Read, Path.Combine(filePath, "ffmpeg.exe")));
                    }
                    else
                    {
                        Expect.Call(fs.FileExists(filePath)).Return(false);
                        Expect.Call(fs.DirectoryExists(filePath)).Return(true);
                        Expect.Call(fs.FileExists(Path.Combine(filePath, "ffmpeg.exe"))).Return(false);
                    }
                }
                else
                {
                    Expect.Call(fs.FileExists(filePath)).Return(false);
                    Expect.Call(fs.DirectoryExists(filePath)).Return(false);
                }
            }
            else
            {
                if (pathAsFileExists)
                {
                    Expect.Call(fs.FileExists(filePath)).Return(true);
                    Expect.Call(() => fs.DemandFileIOPermission(System.Security.Permissions.FileIOPermissionAccess.Read, filePath));
                }
                else
                {
                    Expect.Call(fs.FileExists(filePath)).Return(false);
                    Expect.Call(fs.DirectoryExists(filePath)).Return(false);
                }
            }
            
            bool executionShouldBeSuccessful = (pathIsDirectory && pathAsDirectoryExists && pathAsDirectoryWithAppendedFileExists) || (!pathIsDirectory && pathAsFileExists);

            try
            {
                // Act
                mocks.ReplayAll();
                target.ParseAndVerifyFFMpegPathAttribute(node);

                if (executionShouldBeSuccessful == false)
                    Assert.Fail("No exception was raised when execution should not have been successful.");
            }
            catch (InvalidOperationException ioe)
            {
                // Assert 
                if (executionShouldBeSuccessful)
                    Assert.Fail("InvalidOperationException was incorrectly raised with the following message: {0}", ioe.Message);
            }
            finally
            {
                // Assert 
                mocks.VerifyAll();
            }
        }

        [Test, Combinatorial]
        public void ParseAndVerifyTempFilePathAttribute(
            [Values(true, false)] bool nodeHasAttribute,
            [Values(true, false)] bool pathExists)
        {
            // Arrange
            MockRepository mocks = new MockRepository();

            // - Create arguments for the call.
            string filePath = Guid.NewGuid().ToString();
            

            var doc = new XmlDocument();
            var node = doc.CreateElement("foo");

            if (nodeHasAttribute)
            {
                var attribute = doc.CreateAttribute("tempFilePath");
                attribute.Value = filePath;
                node.Attributes.Append(attribute);
            }

            // - Create dependent mocks.
            var fs = mocks.StrictMock<IFileSystemWrapper>();
            var cntx = mocks.StrictMock<ICSContextWrapper>();
            var target = new VideoPreviewImageModule();

            VideoPreviewImageModule_Accessor accessor = new VideoPreviewImageModule_Accessor(new PrivateObject(target));
            accessor.CurrentFileSystemWrapper = fs;
            accessor.CurrentCSContextWrapper = cntx;

            if (nodeHasAttribute)
            {
                Expect.Call(fs.DirectoryExists(filePath)).Return(pathExists);

                if (pathExists)
                    Expect.Call(() => fs.DemandFileIOPermission(System.Security.Permissions.FileIOPermissionAccess.AllAccess, filePath));
            }
            else
            {
                string mappedPath = Guid.NewGuid().ToString();

                Expect.Call(cntx.MapPath("~/filestorage/")).Return(mappedPath);
                Expect.Call(fs.DirectoryExists(mappedPath)).Return(pathExists);

                if (pathExists)
                    Expect.Call(() => fs.DemandFileIOPermission(System.Security.Permissions.FileIOPermissionAccess.AllAccess, mappedPath));
            }               

            try
            {
                // Act
                mocks.ReplayAll();
                target.ParseAndVerifyTempFilePathAttribute(node);

                if (pathExists == false)
                    Assert.Fail("No exception was raised when execution should not have been successful.");
            }
            catch (InvalidOperationException ioe)
            {
                // Assert 
                if (pathExists)
                    Assert.Fail("InvalidOperationException was incorrectly raised with the following message: {0}", ioe.Message);
            }
            finally
            {
                // Assert 
                mocks.VerifyAll();
            }
        }

        [Test]
        public void DeleteExistingPreviewFilesFromCentralizedFileStorage()
        {
            // Arrange
            string attachmentPath = Guid.NewGuid().ToString();
            string previewFileName = "a";

            MockRepository mocks = new MockRepository();

            // - Create dependent mocks, including stub implementations on the target object.
            Func<string, ICentralizedFile> CreateICentralizedFile = fileName =>
                {
                    var file = mocks.Stub<ICentralizedFile>();
                    SetupResult.For(file.FileName).Return(fileName);
                    SetupResult.For(file.Path).Return(attachmentPath);
                    file.Replay();
                    return file;
                };


            var files = new ICentralizedFile[]
                {
                    CreateICentralizedFile("abc"),
                    CreateICentralizedFile("a12"),
                    CreateICentralizedFile("bcd"),
                    CreateICentralizedFile("123"),
                    CreateICentralizedFile("aef")
                };

            var deletedFiles = new ICentralizedFile[]
            {
                files[0],
                files[1],
                files[4]
            };

            var cfsProvider = mocks.StrictMock<ICentralizedFileStorageProvider>();
            var target = mocks.PartialMock<VideoPreviewImageModule>();
            
            VideoPreviewImageModule_Accessor.CurrentCentralizedFileStorageProvider = cfsProvider;

            Expect.Call(cfsProvider.GetFiles(attachmentPath, PathSearchOption.TopLevelPathOnly)).Return(files);
            deletedFiles.ToList().ForEach(f => Expect.Call(() => cfsProvider.Delete(f.Path, f.FileName)));

            // Act
            mocks.ReplayAll();
            target.DeleteExistingPreviewFilesFromCentralizedFileStorage(attachmentPath, previewFileName);

            // Assert 
            mocks.VerifyAll();
        }
    }
}
